/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 *
 */

import crypto, { BinaryLike } from 'node:crypto';
import os from 'os';
import path from 'path';

import { readJavaDaemonInfoFromCacheFile } from '@ohos/hvigor';
import fse from 'fs-extra';
import WebSocket from 'ws';

import { OhosLogger } from '../../utils/log/ohos-logger';

const _logger = OhosLogger.getLogger('JavaDaemonWebSocket');

export enum MessageType {
  SIGN_APP = 'SIGN_APP',
  VERIFY_PROFILE = 'VERIFY_PROFILE',
  SIGN_SDK = 'SIGN_SDK',
  PACK_APP = 'PACK_APP',
}

interface Resp {
  code: number;
  message: string;
}

export enum ErrorCode {
  SUCCESS = 0,
  FAIL = -1,
  NETWORK_ERROR = 1006,
  CLASS_NOT_FOUND = 1007, // 签名方法不存在，使用的是老jar包等场景
}

export interface SocketResp {
  code: ErrorCode;
  message: string;
}

export interface SocketRequest {
  type: MessageType;
  data?: string;
}

function createHmacSignature(secretKey: string, data: BinaryLike): string {
  const hmac = crypto.createHmac('sha256', secretKey, { encoding: 'utf-8' });
  hmac.update(data);
  return hmac.digest('hex');
}

function readSecretKey(): string | undefined {
  const keyFile = path.join(os.homedir(), '.hvigor', 'daemon', 'cache', 'key.txt');
  if (!fse.existsSync(keyFile)) {
    return undefined;
  }
  return fse.readFileSync(keyFile).toString();
}

export function sendSocketRequest(signRequest: object, toolJarPath: string): Promise<SocketResp> {
  return new Promise((resolve, reject) => {
    const javaDaemonInfo = readJavaDaemonInfoFromCacheFile();
    if (!javaDaemonInfo || !javaDaemonInfo.port) {
      reject({ code: ErrorCode.NETWORK_ERROR, message: 'read java daemon port failed' });
    }
    let queryString = `sessionId=${javaDaemonInfo.sessionId}&jar=${toolJarPath}`;
    const secretKey = readSecretKey();
    if (secretKey) {
      const signature = createHmacSignature(secretKey, queryString);
      queryString += `&signature=${signature}`;
    }
    queryString = encodeURI(queryString);
    const ws = new WebSocket(`ws://127.0.0.1:${javaDaemonInfo.port}?${queryString}`);

    ws.on('error', (err) => {
      reject({ code: ErrorCode.NETWORK_ERROR, message: err.message });
    });

    ws.on('open', () => {
      ws.send(JSON.stringify(signRequest));
    });

    ws.on('close', (code: number, reason: Buffer) => {
      _logger.debug(`java daemon socket close code:${code} reason:${reason.toString()}`);
      if (code !== 1000) {
        reject({ code: ErrorCode.NETWORK_ERROR, message: reason.toString() });
      }
    });

    ws.on('message', (data) => {
      _logger.debug(`java daemon socket received message:${data.toString()}`);
      try {
        const resp: Resp = JSON.parse(data.toString());
        if (resp.code === 0) {
          resolve({ code: ErrorCode.SUCCESS, message: 'ok' });
        } else {
          reject({ code: resp.code, message: resp.message });
        }
      } catch (e: any) {
        reject({ code: ErrorCode.FAIL, message: e.message });
      }
      ws.close(1000, 'close by user');
    });
  });
}
